#include "window.h"
#include "util/assert.h"
#include "input/iinputmanager.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

namespace gecko
{

HWND g_hwnd;
HINSTANCE g_hinstance;

Window::Window()
: m_fullscreenEntered(false)
, m_active(false)
, m_inputManager(NULL)
{
	sprintf(m_windowTitle, "Gecko 1.0 - (");

#if defined(__x86)
	strcat(m_windowTitle, "x86 ");
#elif defined(__x64)
	strcat(m_windowTitle, "x64 ");
#endif

#if defined(_DEBUG)
	strcat(m_windowTitle, " Debug)");
#elif defined(_ARTIST)
	strcat(m_windowTitle, " Artist)");
#elif defined(_QA)
	strcat(m_windowTitle, " QA)");
#elif defined(_RELEASE)
	strcat(m_windowTitle, " Release)");
#endif
}

Window::~Window()
{
}

void Window::Initialise(int width, int height, int bpp, bool fullscreen/*, InputMgr* pInputMgr*/)
{
	m_width = width;
	m_height = height;
	m_bpp = bpp;
	m_fullScreen = fullscreen;

	//this->m_inputMgr = pInputMgr;

	m_hInstance = GetModuleHandle(0);
	
	if ( m_hInstance == NULL )
	{
		ForceCloseAndExit();
	}

	g_hinstance = m_hInstance;

	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= Window::StaticWindowProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= sizeof(Window*);
	wcex.hInstance		= m_hInstance;
	wcex.hIcon			= LoadIcon(m_hInstance, IDI_APPLICATION);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= WINDOWCLASSNAME;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, IDI_APPLICATION);

	RegisterClassEx(&wcex);
	DebugPrint("Registered window class");

	if ( m_fullScreen )
	{
		if ( !CreateFullScreenWindow() )
		{
			if ( !m_fullScreen )
			{
				CreateStandardWindow();
			}
		}
	}
	else
	{
		CreateStandardWindow();
	}

	// initialize the user data field with this object's this pointer,
	// WndProc uses the user data field so that WinProc has access
	if ( !SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG)this) )
	{
		DebugPrint("SetWindowLong() failed");
		ForceCloseAndExit();
	}

	g_hwnd = m_hWnd;
}

bool Window::ChangeDisplayMode()
{
	DEVMODE dmScreenSettings; // Device Mode
	memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
	dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
	dmScreenSettings.dmPelsWidth	= m_width; // Selected Screen Width
	dmScreenSettings.dmPelsHeight	= m_height; // Selected Screen Height
	dmScreenSettings.dmBitsPerPel	= m_bpp; // Selected Bits Per Pixel
	dmScreenSettings.dmDisplayFrequency = 0;
	dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;

	DebugPrint("Attempting resolution change (%dx%dx%d)", m_width, m_height, m_bpp);
	// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
	if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
	{
		char Error[256];
		sprintf_s(Error, "Error switching to fullscreen mode.\n\nThe fullscreen resolution %dx%d is not supported by\nyour video card.\n\nUse windowed mode instead?", m_width, m_height);

		//CLogger::Log("CRendererOGL::CreateFullScreenWindow() - Resolution change failed.\n");
		// If The Mode Fails, Offer Two Options.  Quit Or Run In A Window.
		if (MessageBox(NULL, Error, m_windowTitle, MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
		{
			m_fullScreen = false; // Select Windowed Mode (Fullscreen=FALSE)
			return false;
		}
		else
		{
			// Pop Up A Message Box Letting User Know The Program Is Closing.
			MessageBox(NULL,"Resolution change failed, game will now exit.", m_windowTitle, MB_OK|MB_ICONSTOP);
			DebugPrint("ChangeDisplaySettings() failed");
			ForceCloseAndExit();
		}
	}

	m_fullscreenEntered = true;

	DebugPrint("Resolution change successful");
	return true;
}

LRESULT CALLBACK Window::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	Window * pWindow = 0;

	if (message == WM_NCCREATE)
	{
		pWindow = (Window *) ((LPCREATESTRUCT) lParam)->lpCreateParams ;

		SetWindowLongPtr(hWnd, GWLP_USERDATA, (long) pWindow) ;

		//If you process any messages that are sent before CreateWindowEx returns
		//the HWND, you need something in the place of your HWND member.

		pWindow->m_hWnd = hWnd;
	}
	else
		pWindow = (Window *) GetWindowLongPtr(hWnd, GWLP_USERDATA) ;

	if (pWindow)
		return (pWindow->WindowProc(hWnd, message, wParam, lParam)) ;
	else
		return (DefWindowProc (hWnd, message, wParam, lParam)) ;
}

LRESULT CALLBACK Window::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	Assert(hWnd, "hWnd is NULL");

	switch (message)
	{
	case WM_CHAR:
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_CLOSE:
		CloseWindow();
		break;
	case WM_ACTIVATEAPP:
		m_active = wParam == TRUE ? true : false;

		if ( m_active )
		{
			if ( !m_fullscreenEntered && m_fullScreen )
			{
				ChangeDisplayMode();
			}

			// reacquire DirectInput devices after losing focus to the window
			if ( m_inputManager )
			{
			    m_inputManager->AcquireDevices();
			}
		}
		else
		{
			m_fullscreenEntered = false;

			if ( m_inputManager )
			{
			    m_inputManager->UnacquireDevices();
			}
		}

		break;
	case WM_SYSCOMMAND:
		// prevent screensaver or monitor powersave mode from starting
		// if we are in fullscreen mode
		if ( this->m_fullScreen )
		{
			if (wParam == SC_SCREENSAVE ||
				wParam == SC_MONITORPOWER)
				return 0;
		}
	default:return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}

bool Window::CreateFullScreenWindow()
{
	DebugPrint("Creating full screen window");

	if ( !ChangeDisplayMode() )
	{
		return false;
	}

	m_hWnd = CreateWindowEx(WS_EX_APPWINDOW, // Extended Style For The Window
		WINDOWCLASSNAME, // Class Name
		m_windowTitle, // Window Title
		WS_CLIPSIBLINGS | // Required Window Style
		WS_CLIPCHILDREN | // Required Window Style
		WS_POPUP, // Selected Window Style
		(GetSystemMetrics(SM_CXSCREEN) - m_width) / 2,
		(GetSystemMetrics(SM_CYSCREEN) - m_height) / 2,
		m_width,
		m_height,
		NULL, // No Parent Window
		NULL, // No Menu
		m_hInstance, // Instance
		this); // Don't Pass Anything To WM_CREATE

	if (!m_hWnd)
	{
		MessageBox(NULL, "Can't create fullscreen window.", m_windowTitle, MB_OK|MB_ICONEXCLAMATION);
		DebugPrint("CreateWindowEx() failed");
		return false;
	}

	ShowWindow(m_hWnd, SW_SHOW);
	UpdateWindow(m_hWnd);

	DebugPrint("Full screen window created");

	return true;
}

bool Window::CreateStandardWindow()
{
	DebugPrint("Creating window");

	// windowed app needs to be slightly bigger than the desired resolution
	// due to the windows title bar and such
	RECT r;
	r.left = 0;
	r.top = 0;
	r.right = m_width - 5;
	r.bottom = m_height - 5;
	//AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW | WS_MINIMIZEBOX | WS_SYSMENU, FALSE);

	m_hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window
		WINDOWCLASSNAME, // Class Name
		m_windowTitle, // Window Title
		WS_CLIPSIBLINGS | // Required Window Style
		WS_CLIPCHILDREN | // Required Window Style
		WS_MINIMIZEBOX | WS_SYSMENU, // Selected Window Style
		(GetSystemMetrics(SM_CXSCREEN) - m_width) / 2,
		(GetSystemMetrics(SM_CYSCREEN) - m_height) / 2,
		r.right - r.left,
		r.bottom - r.top,
		NULL, // No Parent Window
		NULL, // No Menu
		m_hInstance, // Instance
		this    ); // Don't Pass Anything To WM_CREATE


	if (!m_hWnd )
	{
		MessageBox(NULL,"Can't create window.", m_windowTitle, MB_OK|MB_ICONEXCLAMATION);
		DebugPrint("CreateWindowEx() failed");
		ForceCloseAndExit();
	}

	ShowWindow(m_hWnd, SW_SHOW);
	UpdateWindow(m_hWnd);

	DebugPrint("Window created");

	return true;
}

bool Window::Destroy()
{
	Assert(m_hInstance, "m_hInstance is NULL");

	DebugPrint("Destroying window");

	// close the window (if not already happened), the window may be
	// closed externally by Alt-F4 (for instance)
	if (m_hWnd)
	{
		DestroyWindow(m_hWnd);
		m_hWnd = 0;
	}

	// unregister the window class
	if (!UnregisterClass(WINDOWCLASSNAME, m_hInstance))
	{
		DebugPrint("UnregisterClass() failed");
		return false;
	}

	DebugPrint("Window destroyed");
	return true;
}

bool Window::Run()
{
	// Now we're ready to receive and process Windows messages.
	MSG  msg;
	BOOL bGotMsg;
	msg.message = WM_NULL;

	// Use PeekMessage() if the app is active, so we can use idle time to
	// render the scene. Else, use GetMessage() to avoid eating CPU time.
	if( m_active )
		bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
	else
		bGotMsg = GetMessage( &msg, NULL, 0U, 0U );

	if( bGotMsg )
	{
		//GetMessage(&msg, NULL, 0, 0);
		// Translate and dispatch the message
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

	if ( WM_QUIT == msg.message )
	{
		return false;
	}
	return true;
}

void Window::CloseWindow()
{
	PostMessage(m_hWnd, WM_QUIT, 0, 0);
}

void Window::ForceCloseAndExit()
{
	// force the app to exit in case of errors
	exit(EXIT_FAILURE);
}

HWND Window::GetWindowHandle()
{
	return m_hWnd;
}

HINSTANCE Window::GetHInstance(void)
{
	return m_hInstance;
}

void Window::ToggleCursor(bool show)
{
	ShowCursor((BOOL)show);
}

void Window::ShowMessageBox( const char* msg )
{
	MessageBox(NULL, msg, m_windowTitle, MB_OK|MB_ICONEXCLAMATION);
}

void Window::GetWindowPosition( int& x, int& y )
{
	RECT rect;
	GetWindowRect(m_hWnd, &rect);

	x = rect.left;
	y = rect.top;
}

int Window::GetWidth()
{
	return m_width;
}

int Window::GetHeight()
{
	return m_height;
}

void Window::SetInputMananger( IInputManager * inputManager )
{
	m_inputManager = inputManager;
}

}
